package com.yunmaozj.tools.lock.interceptor;

import com.yunmaozj.tools.lock.RedisLock;
import com.yunmaozj.tools.lock.annotations.AnnotationDistributedLockAttributeSource;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Method;

/**
 * @author 钟明宏
 * @version V1.0
 * @date 2021-08-29 21:17
 **/
public abstract class AbstractDistributedLockAspectSupport implements BeanFactoryAware, InitializingBean {

    @Nullable
    private RedisLock redisLock;
    @Nullable
    private AnnotationDistributedLockAttributeSource DistributedLockAttributeSource;

    public void setRedisLock(@Nullable RedisLock redisLock) {
        this.redisLock = redisLock;
    }

    protected boolean lockIsInit() {
        return redisLock != null;
    }

    protected Object invokeWithinSync(Method method, @Nullable Class<?> targetClass, Object[] getArguments,
                                      final InvocationCallback invocation) {
        DistributedLockAttribute distributedLockAttribute = DistributedLockAttributeSource.getTransactionAttribute(method, targetClass);
        String businessId = distributedLockAttribute.getBusinessIdGenerator().getBusinessId(method, targetClass, getArguments);
        redisLock.lock(businessId);
        try {
            return invocation.proceedWithInvocation();
        } catch (Throwable throwable) {
            throw new RuntimeException(throwable);
        } finally {
            redisLock.unlock(businessId);
        }
    }

    public void setDistributedLockAttributeSource(@Nullable AnnotationDistributedLockAttributeSource DistributedLockAttributeSource) {
        this.DistributedLockAttributeSource = DistributedLockAttributeSource;
    }

    @FunctionalInterface
    protected interface InvocationCallback {

        @Nullable
        Object proceedWithInvocation() throws Throwable;
    }
}
